08 - Structures

08 - Structures


🔨 🔥 Assignment 🔥 🔨

Create a new project in the way explained in the first instruction.


std::string

std::string is a class that contains a collection of characters and can be used instead of char arrays for storing text. In order to use std::string, an appropriate library has to be included:

#include <string>

//...

std::string text = "Hello world!";
std::cout << text << std::endl;

std::string more_text;
std::cin >> more_text;

Single characters can be accessed using [] operator. String length can be found by using length() method. Simple std::string concatenation is available using + operator.

std::string name = "Alex";
std::string surname = "Black";

std::cout << "First letter of name " << name << " is " << name[0] << std::endl;
std::cout << "This name has " << name.length() << " letters" << std::endl;

std::string full = name + " " + surname;
std::cout << "Full name is: " << full << std::endl;

First letter of name Alex is A
This name has 4 letters
Full name is: Alex Black

Strings can also be easily copied using assignment (=) operator:

std::string copy_of_text = text;

Structure basics

A data structure is a group of data elements grouped together under one name. The most of data structures follow a scheme:

struct TypeName {
    member_type1 member_name1;
    member_type2 member_name2;
    member_type3 member_name3;
    //...
};

where:

For example:

struct Pet {
    std::string name;
    int age;
    double weight;
};

This declares a structure type called Pet, which has three members: name, age, and weight.

For example, the structure objects dog and cat can be declared just like any other type. This declaration creates a new type (Pet), which is then used to declare two objects of this type: dog and cat:

struct Pet {
    std::string name;
    int age;
    double weight;
};

Pet dog, cat;

Once the two objects of a determined structure type are declared (dog and cat) their members can be accessed directly. The syntax for that is simply to insert a dot . between the object name and the member name. For example, we could operate with any of these elements as if they were standard variables of their respective types:

dog.name
dog.age
dog.weight
cat.name
cat.age
cat.weight

Each one of these has the data type corresponding to the member they refer to: dog.name and cat.name are of type char[20], dog.age and cat.age are of type int, while dog.weight and cat.weight are of type double.

Here is an example with structure types:

#include <iostream>

struct Pet
{
   std::string name;
   int age;
   double weight;
};

void print_pet(Pet &pet)
{
   std::cout << "Name: " << pet.name << std::endl;
   std::cout << "Age: " << pet.age << std::endl;
   std::cout << "Weight: " << pet.weight << std::endl;
}

int main()
{
   Pet cat;
   std::cout << "Enter cat's name: ";
   std::cin >> cat.name;
   std::cout << "Enter age: ";
   std::cin >> cat.age;
   std::cout << "Enter weight: ";
   std::cin >> cat.weight;

   Pet dog = {"Henry", 2, 0.5};
   std::cout << "Details about my dog:\n";
   print_pet(dog);
   std::cout << "and cat:\n";
   print_pet(cat);

   Pet copycat = cat;
   std::cout << "Details about my cats copy:\n";
   print_pet(copycat);

   return 0;
}

which will produce following results:

Enter cat’s name: Meow
Enter age: 2
Enter weight: 1.5
Details about my dog:
Name: Henry
Age: 2
Weight: 0.5
and cat:
Name: Meow
Age: 2
Weight: 1.5
Details about my cats copy:
Name: Meow
Age: 2
Weight: 1.5


🔨 🔥 Assignment 🔥 🔨

Create a structure Person that includes the following elements: name, surname, age, weight, and height. In main function create three objects of this type, fill in two of them in the source code and the third one using user input. Create a function that prints object of type Person.


References to the structures

Like any other type, structures can be pointed to by its own reference type:

struct Pet {
    std::string name;
    int age;
    double weight;
};

Pet dog;
Pet &dog_reference = dog;

Here dog is an object of structure type pet, and dog_reference is a reference that points to an object of structure type Pet.


🔨 🔥 Assignment 🔥 🔨

  1. Write a function that returns the structure of type Person, with no arguments. Inside a function ask the user for all information about the person and return filled structure.
  2. In main create a std::vector that stores Person objects. Using emplace_back() method and function for getting information from the user, add 3 elements to the vector of Person. Print all elements of the vector using the function from previous exercise.

Nesting structures

Structures can also be nested in such a way that an element of a structure is itself another structure:

struct Pet {
    std::string name;
    int age;
    double weight;
};

struct Friend {
    std::string name;
    std::string email;
    Pet favourite_pet;
};

After the previous declarations, all of the following expressions would be valid:

Friend gordon;

gordon.name;
gordon.email;
gordon.favourite_pet.name;
gordon.favourite_pet.age;
gordon.favourite_pet.weight;

Splitting the code into multiple files

In order to avoid having one long main file in the project one should split the source code into multiple files. In C/C++ the code must be split into header and source files. Header files (usually .h or .hpp extension) store the declarations while source files (usually .c or .cpp extension) store the definitions.

Header files need to contain #pragma once statement at the very top. This is called “include guard” and prevents the same file content being included multiple times which would lead to redeclaration errors.

Example split of the Pet structure with additional print_pet function:

Pet.hpp

#pragma once

#include <string>

struct Pet {
   std::string name;
   int age;
   double weight;
};

void print_pet(Pet &pet);

Pet.cpp

#include "Pet.hpp"

#include <iostream>

void print_pet(Pet &pet) {
   std::cout << "Name: " << pet.name << std::endl;
   std::cout << "Age: " << pet.age << std::endl;
   std::cout << "Weight: " << pet.weight << std::endl;
}

Main.cpp

#include <iostream>

#include "Pet.hpp"

int main() {
    Pet my_pet;

    my_pet.age = 5;
    my_pet.name = "Mr. Puss";
    my_pet.weight = 3.4;

    print_pet(my_pet);

    return 0;
}

🔨 🔥 Assignment 🔥 🔨

  1. Divide the previous program into 3 independent files:
    • Create a source file Person.cpp including all the functions that read and print data from the previous exercises.
    • Create a header file Person.hpp including the structures declarations and declarations of the the functions from Person.cpp file.
    • Include the header file to the main function file.
  2. Create a function that takes a const reference to a vector of Person objects as an argument and returns average weight of the people. Declare the function in .hpp file, define it in .cpp file. Call it from main() function.
  3. Create a function that returns the tallest person. Pass vector as const reference. Use separate header/source files.

Sorting collections

The algorithm header file defines a set of utility functions designed mainly for collections. In order to use it include appropriate header:

#include <algorithm>

The containers can be sorted using specialized functions. For collections that offer random access (operator [], like std::vector) the sort function can be used.

std::vector<int> collection;
for (int i = 0; i<10; i++)
{
   collection.emplace_back(rand() % 100);
}

std::cout << "Collection before sorting : " << std::endl;

for (auto value : collection) {
   std::cout << value << ", ";
}

std::sort(collection.begin(), collection.end());

std::cout << std::endl << "Collection after sorting : " << std::endl;

for (auto value : collection) {
   std::cout << value << ", ";
}

Sorting collections of structures

By default sort functions “knows” how to sort built-in types - int, double, etc. For structures it is necessary to provide appropriate compare function.

// this example assumes, that Student structure exists and the collection (std::vector) of it exists under name students

std::cout << "Students before sorting: " << std::endl;

for(const auto &student : students) {
   // access using . operator
   std::cout << student.first_name << " ";
   std::cout << student.last_name << ": ";
   std::cout << student.computer_science_grade << std::endl;
}

sort(students.begin(), students.end(), compare_students_grades);

std::cout << "Students after sorting: " << std::endl;

for(const auto &student : students) {
   // access using . operator
   std::cout << student.first_name << " ";
   std::cout << student.last_name << ": ";
   std::cout << student.computer_science_grade << std::endl;
}

The example above requires compare_students_grades function to be defined before using it:

bool compare_students_grades(const Student &s1, const Student &s2) {
    return s1.computer_science_grade > s2.computer_science_grade);
}

🔨 🔥 Assignment 🔥 🔨

Create a function that sorts all the people in the array by their age. Use separate header/source files.


Homework 💥 🏠

  1. Create structure Athlete that stores information about the athlete (first name, last name, bench press personal best, deadlift personal best). Create three athletes using values in the source code. Read information about the fourth athlete from the user. The information about the athletes should be stored in a vector. Create a function to print information about Athlete.
  2. Modify the code from task 1. Reading the information about the athlete should be done in a function that takes one parameter - a reference to the athlete. Modify the code used to print information about the athlete to take one parameter - a const reference to the athlete.
  3. In task 3, divide your code into three files:
    • Create file Athlete.cpp containing the implementation of the functions used to read and print information about the athlete.
    • Create a header file Athlete.h containing the definition of the structure and declarations of the functions.
    • Properly join those files in order to obtain a working solution.
  4. Write a function that takes a vector of athletes (by const reference) and returns the best personal best record in bench press among the evaluated athletes.
  5. Write a function that takes a vector of athletes (by const reference) and returns the average personal best records in deadlift.
  6. Write a function that will take a vector of athletes and will sort them according to the potential measured as a sum of the personal best records in bench press and deadlift. Print the information about athletes starting with the athlete with the greatest potential.

Authors: Michał Fularz, Dominik Pieczyński, Tomasz Mańkowski, Jakub Tomczyński